package com.smart.community.commons.aspect;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import com.alibaba.fastjson.JSON;
import com.smart.community.commons.annotation.ApiLog;
import com.smart.community.commons.config.sender.LogSender;
import com.smart.community.commons.dto.ApiLogDTO;
import com.smart.community.commons.utils.SecurityUtils;
import com.smart.community.commons.constants.RequestHeaderConstants;

import lombok.extern.slf4j.Slf4j;

@Aspect
@Component
@Slf4j
public class ApiLogAspect {

    @Autowired
    private LogSender logSender; // 注入RocketMQ实现的LogSender

    // 切点：拦截所有带@ApiLog注解的方法
    @Pointcut("@annotation(com.smart.community.commons.annotation.ApiLog)")
    public void apiLogPointcut() {}

    @Around("apiLogPointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        // 1. 初始化日志DTO
        ApiLogDTO logDTO = ApiLogDTO.builder().build();
        long startTime = System.currentTimeMillis(); // 记录开始时间

        try {
            // 2. 解析请求信息
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes != null) {
                HttpServletRequest request = attributes.getRequest();
                // 设置请求相关信息
                logDTO.setRequestUrl(request.getRequestURL().toString());
                logDTO.setRequestMethod(request.getMethod());
                logDTO.setIpAddress(request.getRemoteAddr());
                logDTO.setUserAgent(request.getHeader("User-Agent"));
            }

            // 3. 解析注解信息
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            ApiLog apiLog = method.getAnnotation(ApiLog.class);

            // 设置注解属性到DTO
            logDTO.setLogTitle(apiLog.logTitle());
            logDTO.setLogType(apiLog.logType());
            logDTO.setModuleName(apiLog.moduleName());
            logDTO.setOperationName(apiLog.operationType().getName()); // 操作类型名称
            logDTO.setOperationDesc(apiLog.operationType().getDesc()); // 操作类型描述
            
            // 获取登录用户信息，然后设置`user_id`、`username`、`operator`、`create_by`等字段
            try {
                // 优先从用户上下文获取用户信息（网关注入）
                Long userId = SecurityUtils.getCurrentUserId();
                String username = SecurityUtils.getCurrentUsername();
                String nickname = SecurityUtils.getCurrentNickname();
                String userType = SecurityUtils.getCurrentUserType();
                
                // 如果用户上下文为空，尝试从请求头获取用户信息（网关传递）
                if (userId == null && attributes != null) {
                    HttpServletRequest request = attributes.getRequest();
                    String userIdStr = request.getHeader(RequestHeaderConstants.USER_ID);
                    String usernameHeader = request.getHeader(RequestHeaderConstants.USER_NAME);
                    String realNameHeader = request.getHeader(RequestHeaderConstants.REAL_NAME);
                    String userTypeHeader = request.getHeader(RequestHeaderConstants.USER_TYPE);
                    String userRoleHeader = request.getHeader(RequestHeaderConstants.USER_ROLE);
                    String userPermissionsHeader = request.getHeader(RequestHeaderConstants.USER_PERMISSIONS);
                    String superAdminHeader = request.getHeader(RequestHeaderConstants.SUPER_ADMIN);
                    
                    if (userIdStr != null && !userIdStr.trim().isEmpty()) {
                        try {
                            userId = Long.valueOf(userIdStr.trim());
                            log.debug("从请求头获取用户ID: {}", userId);
                        } catch (NumberFormatException e) {
                            log.warn("请求头中的用户ID格式不正确: {}", userIdStr);
                        }
                    }
                    
                    if (usernameHeader != null && !usernameHeader.trim().isEmpty()) {
                        username = usernameHeader.trim();
                        log.debug("从请求头获取用户名: {}", username);
                    }
                    
                    if (realNameHeader != null && !realNameHeader.trim().isEmpty()) {
                        nickname = realNameHeader.trim();
                        log.debug("从请求头获取真实姓名: {}", nickname);
                    }
                    
                    if (userTypeHeader != null && !userTypeHeader.trim().isEmpty()) {
                        userType = userTypeHeader.trim();
                        log.debug("从请求头获取用户类型: {}", userType);
                    }
                    
                    // 记录用户角色和权限信息（用于调试）
                    if (userRoleHeader != null && !userRoleHeader.trim().isEmpty()) {
                        log.debug("从请求头获取用户角色: {}", userRoleHeader);
                    }
                    
                    if (userPermissionsHeader != null && !userPermissionsHeader.trim().isEmpty()) {
                        log.debug("从请求头获取用户权限: {}", userPermissionsHeader);
                    }
                    
                    if (superAdminHeader != null && !superAdminHeader.trim().isEmpty()) {
                        log.debug("从请求头获取超级管理员标识: {}", superAdminHeader);
                    }
                }
                
                // 如果仍然为空，尝试从Sa-Token获取（兼容性处理）
                if (userId == null) {
                    userId = SecurityUtils.getCurrentUserId();
                    log.debug("从Sa-Token获取用户ID: {}", userId);
                }
                
                if (username == null && userId != null) {
                    // 如果用户名为空但有用户ID，尝试从Sa-Token获取用户名
                    try {
                        // 这里可以根据实际业务需求调用用户服务获取用户名
                        // 暂时使用用户ID作为用户名
                        username = "用户" + userId;
                        log.debug("使用用户ID作为用户名: {}", username);
                    } catch (Exception e) {
                        log.warn("获取用户名失败，用户ID: {}", userId, e);
                        username = "未知用户";
                    }
                }
                
                // 设置用户相关字段
                logDTO.setUserId(userId);
                logDTO.setUsername(username != null ? username : "未知用户");
                logDTO.setUserType(userType != null ? userType : "未知类型");
                logDTO.setOperator(nickname != null ? nickname : username != null ? username : "未知操作人");
                logDTO.setCreateBy(userId);
                
                log.debug("设置用户信息到日志DTO - 用户ID: {}, 用户名: {}, 操作人: {}, 用户类型: {}", 
                    userId, username, logDTO.getOperator(), userType);
                    
            } catch (Exception e) {
                log.warn("获取用户信息失败，将使用默认值", e);
                // 设置默认值
                logDTO.setUserId(null);
                logDTO.setUsername("未知用户");
                logDTO.setUserType("未知类型");
                logDTO.setOperator("未知操作人");
                logDTO.setCreateBy(null);
            }

            // 4. 记录请求参数（使用安全的序列化方法）
            Object[] args = joinPoint.getArgs();
            logDTO.setRequestParams(serializeArgsSafely(args));

            // 5. 执行目标方法
            Object result = joinPoint.proceed();

            // 6. 记录成功结果
            logDTO.setResponseData(JSON.toJSONString(result));
            logDTO.setResponseResult("成功");
            logDTO.setLogLevel(2); // INFO级别（2对应表中定义的INFO）
            logDTO.setStatus(1); // 状态：正常
            return result;

        } catch (Exception e) {
            // 7. 记录异常信息
            logDTO.setResponseResult("失败");
            logDTO.setErrorMessage(e.getMessage());
            logDTO.setStackTrace(e.getStackTrace().toString());
            logDTO.setLogLevel(4); // ERROR级别（4对应表中定义的ERROR）
            logDTO.setStatus(0); // 状态：异常
            throw e; // 继续抛出异常，不影响业务处理
        } finally {
            // 8. 补充其他信息
            long endTime = System.currentTimeMillis();
            logDTO.setExecutionTime((int) (endTime - startTime)); // 执行时间（毫秒）
            logDTO.setLogTime(new Date());
            logDTO.setCreateTime(new Date());
            logDTO.setOperationTime(new Date());

            // 9. 通过日志发送器发送日志（异步发送，不阻塞主流程）
            try {
                logSender.sendLog(logDTO);
            } catch (Exception e) {
                // 日志发送失败不影响主业务流程
                log.warn("日志发送失败，但不影响主业务流程: {}", e.getMessage());
            }
        }
    }

    /**
     * 安全地序列化方法参数，避免序列化不可序列化的对象
     * 
     * @param args 方法参数数组
     * @return 序列化后的JSON字符串
     */
    private String serializeArgsSafely(Object[] args) {
        if (args == null || args.length == 0) {
            return "[]";
        }

        try {
            Object[] safeArgs = new Object[args.length];
            for (int i = 0; i < args.length; i++) {
                Object arg = args[i];
                if (arg instanceof MultipartFile) {
                    // 处理MultipartFile对象 - 只记录基本信息
                    MultipartFile file = (MultipartFile) arg;
                    Map<String, Object> fileInfo = new HashMap<>();
                    fileInfo.put("type", "MultipartFile");
                    fileInfo.put("originalFilename", file.getOriginalFilename());
                    fileInfo.put("contentType", file.getContentType());
                    fileInfo.put("size", file.getSize());
                    fileInfo.put("isEmpty", file.isEmpty());
                    safeArgs[i] = fileInfo;
                } else if (arg instanceof MultipartFile[]) {
                    // 处理MultipartFile数组 - 只记录基本信息
                    MultipartFile[] files = (MultipartFile[]) arg;
                    Object[] fileInfos = new Object[files.length];
                    for (int j = 0; j < files.length; j++) {
                        MultipartFile file = files[j];
                        Map<String, Object> fileInfo = new HashMap<>();
                        fileInfo.put("type", "MultipartFile");
                        fileInfo.put("originalFilename", file.getOriginalFilename());
                        fileInfo.put("contentType", file.getContentType());
                        fileInfo.put("size", file.getSize());
                        fileInfo.put("isEmpty", file.isEmpty());
                        fileInfos[j] = fileInfo;
                    }
                    safeArgs[i] = fileInfos;
                } else if (arg instanceof HttpServletRequest) {
                    // 处理HttpServletRequest对象 - 只记录基本信息
                    HttpServletRequest request = (HttpServletRequest) arg;
                    Map<String, Object> requestInfo = new HashMap<>();
                    requestInfo.put("type", "HttpServletRequest");
                    requestInfo.put("method", request.getMethod());
                    requestInfo.put("requestURI", request.getRequestURI());
                    requestInfo.put("contentType", request.getContentType());
                    safeArgs[i] = requestInfo;
                } else if (arg instanceof HttpServletResponse) {
                    // 处理HttpServletResponse对象 - 只记录基本信息
                    Map<String, Object> responseInfo = new HashMap<>();
                    responseInfo.put("type", "HttpServletResponse");
                    safeArgs[i] = responseInfo;
                } else {
                    // 其他对象直接使用原对象
                    safeArgs[i] = arg;
                }
            }
            
            return JSON.toJSONString(safeArgs);
        } catch (Exception e) {
            log.warn("参数序列化失败，使用简化信息: {}", e.getMessage());
            // 如果序列化失败，返回简化的参数信息
            Map<String, Object> fallbackInfo = new HashMap<>();
            fallbackInfo.put("message", "参数序列化失败");
            fallbackInfo.put("error", e.getMessage());
            fallbackInfo.put("paramCount", args.length);
            return JSON.toJSONString(fallbackInfo);
        }
    }
}